module fn.pattern.Collection

open System.Collections.Generic;



type Map<'a,'b when 'a : comparison> with

    static member cabinet (iterator : ('a * 'b) seq) =
        let m = Map.empty
        let accum (m : Map<_,_ list>) (k, v) =
            m.Add(k, match m.TryFind(k) with
                     | Some l -> List.append l [v]
                     | None -> [v])
        Seq.fold accum m iterator

    static member ofDictionary dc : Map<'a,'b> = 
        Map.ofSeq (seq { for KeyValue(k,v) in dc -> (k,v) })

    static member join (maps : Map<'a,'b> seq) =
        Seq.map Map.toSeq maps |> Seq.concat |> Map.ofSeq


module Seq =
    let naturals = Seq.initInfinite id
    let enumerate sq = Seq.zip naturals sq

    let first sequence = Seq.nth 0 sequence
    let last sequence = Seq.reduce (fun _ i -> i) sequence
            
    let repeatedly f =
        let r state =
            let y = f state
            Some (y, y)
        Seq.unfold r

    let takeUntil condition sequence =
        let prefix = Seq.takeWhile (fun x -> not (condition x)) sequence
        if Seq.length prefix < Seq.length sequence then
            let one_more = Seq.nth (Seq.length prefix) sequence
            Seq.concat [|prefix; Seq.singleton one_more|]
        else
            prefix
            
            
module List =
    let pairs l =
        let rec loop acc = function
            | [] | [_] -> List.rev acc
            | h1 :: h2 :: tl -> loop ((h1, h2) :: acc) tl
        loop [] l


type HashSet<'a> with

    static member union (a : HashSet<'a>) (b : HashSet<'a>) =
        let c = HashSet(a)
        c.UnionWith(b)
        c

    static member intersection (a : HashSet<'a>) (b : HashSet<'a>) =
        let c = HashSet(a)
        c.IntersectWith(b)
        c



type Flag () =
    let mutable value = false

    member this.set() = value <- true
    member this.isSet() = value
    member this.setIf b = if b then this.set()
